#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>


uint32_t GetLeU32Num(uint8_t* x);
int32_t GetLeI32Num(uint8_t* x);
uint16_t GetLeU16Num(uint8_t* x);

typedef struct
{
	uint8_t* rawMem;
	uint32_t memSize;
	uint8_t* currentPointer;


} StreamIoManager;

typedef enum
{
	NUMBER_I8 = 0,
	NUMBER_U8,
	NUMBER_U16,
	NUMBER_I16,
	NUMBER_U32,
	NUMBER_I32,
	BOOLEAN,
	STRING,
	BLOB,
	DATETIME
} ParamType;

typedef struct
{
	uint8_t* configDataHead;
	uint32_t configDataLength;
	uint32_t configDataHash;
} ConfigDataAccessParam;

ConfigDataAccessParam g_ConfigDataAccessParam;

#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
	|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
#endif

#if !defined (get16bits)
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
	+ (uint32_t)(((const uint8_t *)(d))[0]))
#endif

uint32_t SuperFastHash(const char * data, int len)
{
	uint32_t hash = len, tmp;
	int rem;

	if (len <= 0 || data == NULL) return 0;

	rem = len & 3;
	len >>= 2;

	/* Main loop */
	for (; len > 0; len--)
	{
		hash += get16bits(data);
		tmp = (get16bits(data + 2) << 11) ^ hash;
		hash = (hash << 16) ^ tmp;
		data += 2 * sizeof (uint16_t);
		hash += hash >> 11;
	}

	/* Handle end cases */
	switch (rem)
	{
	case 3:
		hash += get16bits(data);
		hash ^= hash << 16;
		hash ^= ((signed char)data[sizeof (uint16_t)]) << 18;
		hash += hash >> 11;
		break;
	case 2:
		hash += get16bits(data);
		hash ^= hash << 11;
		hash += hash >> 17;
		break;
	case 1:
		hash += (signed char)*data;
		hash ^= hash << 10;
		hash += hash >> 1;
	}

	/* Force "avalanching" of final 127 bits */
	hash ^= hash << 3;
	hash += hash >> 5;
	hash ^= hash << 4;
	hash += hash >> 17;
	hash ^= hash << 25;
	hash += hash >> 6;

	return hash;
}

void ConfigDataAccessParamInit(uint8_t* rawDataPtr, ConfigDataAccessParam* configDataAccessParam)
{
	if ((rawDataPtr[0] == 'Y') && (rawDataPtr[1] == 'C') && (rawDataPtr[2] == 'F') && (rawDataPtr[3] == 'G'))
	{
		printf("Correct identifier YCFG detected.\n");
		rawDataPtr += sizeof(uint32_t);
		configDataAccessParam->configDataLength = GetLeU32Num(rawDataPtr);
		rawDataPtr += sizeof(uint32_t);
		configDataAccessParam->configDataHash = GetLeU32Num(rawDataPtr);
		rawDataPtr += sizeof(uint32_t);
		configDataAccessParam->configDataHead = rawDataPtr;

		if (SuperFastHash(configDataAccessParam->configDataHead, configDataAccessParam->configDataLength) == configDataAccessParam->configDataHash)
		{
			printf("Hash is OK.\n");
		}
	}
}

uint32_t GetLeU32Num(uint8_t* x)
{
	return ((0xFF000000 & ((*(x + 3)) << 24)) | (0x00FF0000 & ((*(x + 2)) << 16)) | (0x0000FF00 & ((*(x + 1)) << 8)) | (0x000000FF & (*x)));
}

int32_t GetLeI32Num(uint8_t* x)
{
	return ((0xFF000000 & ((*(x + 3)) << 24)) | (0x00FF0000 & ((*(x + 2)) << 16)) | (0x0000FF00 & ((*(x + 1)) << 8)) | (0x000000FF & (*x)));
}

uint16_t GetLeU16Num(uint8_t* x)
{
	return ((0x00FF & (*x)) | (0xFF00 & (*(x + 1) << 8)));
}



void StreamIoInit(StreamIoManager* manager, uint8_t* mem, uint32_t size)
{
	manager->rawMem = mem;
	manager->memSize = size;
	manager->currentPointer = manager->rawMem;
}

uint8_t StreamIoReadByte(StreamIoManager* manager)
{
	if ((manager->currentPointer) < (manager->rawMem + manager->memSize))
	{
		return (*((manager->currentPointer)++));
	}
	else
	{
		return 0;
	}
}

uint8_t StreamIoPeekByte(StreamIoManager* manager)
{
	if ((manager->currentPointer) < (manager->rawMem + manager->memSize))
	{
		return (*(manager->currentPointer));
	}
	else
	{
		return 0;
	}
}

uint32_t CmpItemName(const char *itemName, uint8_t** readPtrPtr)
{
	uint8_t reqItemNameLen = strlen(itemName);
	uint8_t currentItemNameLen = (**readPtrPtr);
	uint32_t i;
	(*readPtrPtr)++; // Move to ItemName string
	if (reqItemNameLen != currentItemNameLen)
	{
		(*readPtrPtr) += (currentItemNameLen);
		return 0;
	}
	else
	{
		for (i = 0; i < reqItemNameLen; i++)
		{
			if ((*itemName) != (**readPtrPtr))
			{
				break;
			}
			(*readPtrPtr)++;
			itemName++;
		}
		if (i != reqItemNameLen)
		{
			(*readPtrPtr) += (reqItemNameLen - i);
			return 0;
		}
		return 1;
	}
}

ParamType GetItemType(uint8_t** readPtrPtr)
{
	ParamType type;
	type = (ParamType)(**readPtrPtr);
	(*readPtrPtr)++;
	return type;
}

uint32_t GetItemLength(ParamType itemType, uint8_t** readPtrPtr)
{
	uint32_t itemLen;
	if ((itemType == STRING) || (itemType == BLOB))
	{
		itemLen = GetLeU16Num(*readPtrPtr);
		(*readPtrPtr) += 2;
	}
	else
	{
		itemLen = (**readPtrPtr);
		(*readPtrPtr)++;
	}
	return itemLen;
}
uint8_t* FindItemPostion(const char* itemName)
{
	ParamType itemType;
	uint32_t itemLen;
	uint8_t* readPtr;

	readPtr = g_ConfigDataAccessParam.configDataHead;

	do
	{
		if (CmpItemName(itemName, &readPtr))
		{
			return readPtr;
		}
		else
		{
			itemType = GetItemType(&readPtr);
			itemLen = GetItemLength(itemType, &readPtr);
			readPtr += itemLen;
		}
	} while (readPtr < (g_ConfigDataAccessParam.configDataHead + g_ConfigDataAccessParam.configDataLength));
	return NULL;
}

uint32_t ReadStringValueLen(const char* itemName)
{
	uint8_t* posPtr = FindItemPostion(itemName);
	uint32_t len;
	if (posPtr != NULL)
	{
		ParamType itemType = GetItemType(&posPtr);
		if (itemType != STRING)
		{
			return 0;
		}
		else
		{
			len = GetItemLength(itemType, &posPtr);
		}

		return len;
	}
	else
	{
		return 0;
	}
}

void ReadStringValue(const char* itemName, char* strBuffer, uint32_t maxLen)
{
	uint8_t* posPtr = FindItemPostion(itemName);
	uint32_t len;
	if (posPtr != NULL)
	{
		ParamType itemType = GetItemType(&posPtr);
		if (itemType != STRING)
		{

		}
		else
		{
			len = GetItemLength(itemType, &posPtr);
			memcpy(strBuffer, posPtr, maxLen < len ? maxLen : len);
		}
	}
	else
	{

	}
}


int32_t ReadI32Value(const char* itemName)
{
	uint8_t* posPtr = FindItemPostion(itemName);
	int32_t i32Value;
	if (posPtr != NULL)
	{
		ParamType itemType = GetItemType(&posPtr);
		if (itemType != NUMBER_I32)
		{
			return 0;
		}
		else
		{
			GetItemLength(itemType, &posPtr);
			i32Value = GetLeI32Num(posPtr);
		}
		return i32Value;
	}
	else
	{
		return 0;
	}
}

uint32_t ReadU32Value(const char* itemName)
{
	uint8_t* posPtr = FindItemPostion(itemName);
	uint32_t u32Value;
	if (posPtr != NULL)
	{
		ParamType itemType = GetItemType(&posPtr);
		if (itemType != NUMBER_U32)
		{
			return 0;
		}
		else
		{
			GetItemLength(itemType, &posPtr);
			u32Value = GetLeU32Num(posPtr);
		}

		return u32Value;
	}
	else
	{
		return 0;
	}
}

void ReadBlobMemPointer(const char* itemName,uint8_t* blobMemPointer,uint32_t* size)
{

}

void ReadBlobToMem(const char* itemName, uint8_t* blobMemPointer, uint32_t* size)
{

}


int main()
{
	uint8_t* pFileBuf;
	uint32_t fileSize;
	char fileName[300];
	char preFileName[] = "a.YuansCfg";

	FILE *fp;

start:
	//    printf("Input file name: ");
	//    gets(fileName);
	//    if(!fileName[0])
	//        return;

	if ((fp = fopen(preFileName, "rb")) == NULL)
	{
		printf("Can not open file\n");
		goto start;
	}

	fseek(fp, 0, SEEK_END);
	fileSize = ftell(fp);
	fseek(fp, 0, SEEK_SET);

	printf("------------Start------------\n\n\n");
	printf("File Size: %d\n", fileSize);
	pFileBuf = malloc(fileSize);
	fread(pFileBuf, sizeof(uint8_t), fileSize, fp);

	ConfigDataAccessParamInit(pFileBuf, &g_ConfigDataAccessParam);
	printf("0x%8x\n", ReadI32Value("Pic32Ver"));

	char buf[50];
	ReadStringValue("VelCurveNote", buf, sizeof(buf));

	printf("%s\n", buf);

	getchar();

	return 0;
}
